"""
@project: PyCharm
@author: MZM
@file: admin.py
@ide: PyCharm
@time: 2021/6/29 14:27
@desc： 
"""
import csv
import datetime
import json
import socket

from django.contrib import admin
from django.contrib.admin import AdminSite
from django.core.files import File
from django.http import JsonResponse
from import_export import resources
from import_export.admin import ImportExportActionModelAdmin, ImportExportModelAdmin
from ruamel import yaml
from simpleui.admin import AjaxAdmin

from .forms import ApiImportForm, ApiConfirmImportForm
from .models import *
import os

from .tasks import runstep_task, runtestcase_task, runtestsuite_task
import logging

AdminSite.site_header = "dahttprunner极速版"
AdminSite.index_title = "dahttprunner极速版"
AdminSite.site_title= "dahttprunner极速版"

logger = logging.getLogger(__name__)
filedir = os.path.dirname(__file__)
ip = socket.gethostbyname(socket.gethostname())
if os.path.exists(f'./runner/data/'):
    os.mkdir(f'./runner/data/')


@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
    # 注册自定义模块
    list_display = ['name','version']

    def save_model(self, request, obj, form, change):
        if change is False:
            obj.creater = request.user
        super().save_model(request, obj, form, change)

@admin.register(ApiGroup)
class ApiGroupAdmin(admin.ModelAdmin):
    # 注册自定义模块
    list_display = ['name']

    def save_model(self, request, obj, form, change):
        if change is False:
            obj.creater = request.user
        super().save_model(request, obj, form, change)

@admin.register(Header)
class HeaderAdmin(admin.ModelAdmin):
    # 注册自定义模块
    pass



# class Jsonkeyinline(admin.TabularInline):
#     '''
#     排序运行测试用例的内联关系
#     '''
#     model = ApiJson
#     extra = 0

    # @admin.register(JsonKey)
    # class JsonKeyAdmin(admin.ModelAdmin):
    #     # 注册自定义模块
    #     pass

class ApiResource(resources.ModelResource):
    class Meta:
        model = Api
        exclude = ('updatetime','createtime')

    def import_row(self, row, instance_loader, using_transactions, dry_run, raise_errors, **kwargs):
        row['group'] = ApiGroup.objects.get(name=row['group']).id
        row.update({'project': kwargs['project']})
        row.update({'creater': kwargs['user'].id})
        row.update({'header': kwargs['header']})
        return super().import_row(row, instance_loader, using_transactions, dry_run, raise_errors, **kwargs)


@admin.register(Api)
class ApiAdmin(ImportExportModelAdmin,AjaxAdmin):
    # 注册api管理模块
    resource_class = ApiResource
    list_display = ['name','creater','group']
    filter_horizontal = ['header']
    exclude = ['creater']
    search_fields = ['name','url']
    # inlines = [Jsonkeyinline, ]

    def save_model(self, request, obj, form, change):
        if change is False:
            obj.creater = request.user
        super().save_model(request, obj, form, change)

    def get_import_form(self):
        '''
        导入预览选择对象自定义表单
        :return:
        '''
        return ApiImportForm

    def get_confirm_import_form(self):
        '''
        导入选择对象自定义表单
        :return:
        '''
        return ApiConfirmImportForm

    def get_form_kwargs(self, form, *args, **kwargs):
        '''导入预览时导入数据加入表单字段值'''
        if isinstance(form, (ApiImportForm,ApiConfirmImportForm)):
            if form.is_valid():
                project = form.cleaned_data['project']
                kwargs.update({'project': project.id})
                header = form.cleaned_data['header']
                headerlist = [str(id) for id in header.values_list('id',flat=True)]
                kwargs.update({'header': ','.join(headerlist)})
        return kwargs

    def get_import_data_kwargs(self,request, form, *args, **kwargs):
        '''导入时赋值对应字段给表单字段值进行保存'''
        if isinstance(form, (ApiImportForm,ApiConfirmImportForm)):
            if form.is_valid():
                project = form.cleaned_data['project']
                kwargs.update({'project': project.id})
                header = form.cleaned_data['header']
                headerlist = [str(id) for id in header.values_list('id',flat=True)]
                kwargs.update({'header': ','.join(headerlist)})
                return kwargs
        return super().get_import_data_kwargs(request, form=form, *args, **kwargs)


class Variablesinline(admin.TabularInline):
    '''
    排序运行测试用例的内联关系
    '''
    model = Variables_through
    extra = 0

    @admin.register(Variablekey)
    class VariablekeyAdmin(admin.ModelAdmin):
        # 注册自定义模块
        pass

        def has_module_permission(self, request):
            return False

class Cookiesinline(admin.TabularInline):
    '''
    排序运行测试用例的内联关系
    '''
    model = Cookies_through
    extra = 0

    @admin.register(Cookieskey)
    class CookieskeyAdmin(admin.ModelAdmin):
        # 注册自定义模块
        pass

        def has_module_permission(self, request):
            return False


class Extractinline(admin.TabularInline):
    '''
    排序运行测试用例的内联关系
    '''
    model = Extract_through
    extra = 0

    @admin.register(Extractkey)
    class ExtractkeyAdmin(admin.ModelAdmin):
        # 注册自定义模块
        search_fields = ['value']

        def has_module_permission(self, request):
            return False

    @admin.register(Extractval)
    class ExtractvalAdmin(admin.ModelAdmin):
        # 注册自定义模块
        search_fields = ['value']

        def has_module_permission(self, request):
            return False

class Validatesinline(admin.TabularInline):
    '''
    排序运行测试用例的内联关系
    '''
    model = Validates_through
    extra = 0
    autocomplete_fields = ['validatekey','validateval']

    @admin.register(Validatekey)
    class ValidatekeyAdmin(admin.ModelAdmin):
        # 注册自定义模块
        search_fields = ['value']

        def has_module_permission(self, request):
            return False

    @admin.register(Validateval)
    class ValidatevalAdmin(admin.ModelAdmin):
        # 注册自定义模块
        search_fields = ['value']

        def has_module_permission(self, request):
            return False

@admin.register(Hook)
class HookAdmin(admin.ModelAdmin):
    # 注册自定义模块
    pass

@admin.register(BASEURL)
class BASEURLAdmin(admin.ModelAdmin):
    # 注册自定义模块
    list_display = ['name','url']

def get_paramval(s,type):
    # 根据校验参数值的值和类型生成数据
    if type == 'int':
        return int(s)
    elif type == 'str':
        return s
    elif type == 'True':
        return True
    elif type == 'False':
        return False
    elif type == 'object':
        return json.loads(s)

class TestStepResource(resources.ModelResource):
    class Meta:
        model = TestStep
        exclude = ('updatetime','createtime')

def get_teststep(obj):
    teststep = {}
    # 获取套件所有的测试用例，结果为[[{套件1用例},...],[{套件2用例},...]]
    headers = {}
    for hobj in obj.api.header.all():
        headers[hobj.key] = hobj.value

    variables = {}
    for vobj in Variables_through.objects.filter(teststep=obj):
        variables[vobj.variablekey.value] = vobj.variableval

    data = obj.body

    cookies = {}
    for cobj in Cookies_through.objects.filter(teststep=obj):
        cookies[cobj.cookieskey.value] = cobj.cookiesval

    data = obj.body

    extract = {}
    for eobj in Extract_through.objects.filter(teststep=obj):
        extract[eobj.extractkey.value] = eobj.extractval.value

    validates = []
    for vobj in Validates_through.objects.filter(teststep=obj):
        validates.append(
            {'check': vobj.validatekey.value, 'comparator': vobj.mode, 'expect': get_paramval(vobj.validateval.value,vobj.validateval.type)})

    setup_hooks = []
    for shobj in obj.setup_hooks.all():
        setup_hooks.append(shobj.name)
    teardown_hooks = []
    for thobj in obj.teardown_hooks.all():
        teardown_hooks.append(thobj.name)
    teststep['name'] = obj.name
    teststep['request'] = {}
    teststep['variables'] = variables
    teststep['request']['mode'] = obj.mode
    teststep['request']['cookies'] = cookies
    teststep['request']['url'] = obj.api.url
    teststep['request']['method'] = obj.api.method
    teststep['request']['headers'] = headers
    teststep['request']['data'] = data
    teststep['allow_redirects'] = obj.allow_redirects
    teststep['extract'] = extract
    teststep['validate'] = validates
    teststep['setup_hooks'] = setup_hooks
    teststep['teardown_hooks'] = teardown_hooks
    return teststep


@admin.register(TestStep)
class TestStepAdmin(ImportExportActionModelAdmin, AjaxAdmin):
    # 注册api管理模块
    resource_class = TestStepResource
    list_display = ['name','creater','api']
    inlines = [Variablesinline,Cookiesinline,Extractinline,Validatesinline,]
    actions = ['copy','gen_yaml','runstep']
    search_fields = ['name']
    exclude = ['creater']
    list_filter = ['api','creater']
    autocomplete_fields = ['api']

    def save_model(self, request, obj, form, change):
        if change is False:
            obj.creater = request.user
        super().save_model(request, obj, form, change)

    def copy(self,request,query_set):
        '''
        # 复制测试用例
        '''
        for obj in query_set:
            oid = obj.did
            obj.id = None
            obj.name += 'COPY'
            obj.save()
            # 将原对象的外联对象与新对象关联
    copy.short_description = '复制'

    def gen_teststep(self,request,query_set):
        post = request.POST
        config = {}
        config['name'] = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
        config['base_url'] = BASEURL.objects.get(id=post.get('baseurl')).url
        config['export'] = []
        config['variables'] = {}
        for obj in query_set:
            teststep = get_teststep(obj)
        teststeps = {'suitename':'测试步骤','config': config, 'teststeps': [teststep], 'parameters':{}}
        return teststeps


    def gen_yaml(self,request,query_set):
        '''
        获取选中测试集合中的用例数据拼接为数组
        :param request:
        :param query_set:
        :return:
        '''
        try:
            teststeps = self.gen_teststep(request,query_set)
            with open(filedir + f'/runner/data/test.yaml', 'w', encoding='utf-8') as f:
                yaml.dump([teststeps], f, Dumper=yaml.RoundTripDumper)
        except Exception as e:
            return JsonResponse(data={
                'status': 'error',
                'msg': f'异常！{e}'
            })
        return JsonResponse(data={
            'status': 'success',
            'msg': '处理成功！'
        })
    gen_yaml.short_description = '生成文件'
    gen_yaml.layer = {
            # 这里指定对话框的标题
            'title': '生成文件',
            # 提示信息
            'tips': '生成YAML文件',
            # 确认按钮显示文本
            'confirm_button': '确定',
            # 取消按钮显示文本
            'cancel_button': '取消',

            # 弹出层对话框的宽度，默认50%
            'width': '40%',

            # 表单中 label的宽度，对应element-ui的 label-width，默认80px
            'labelWidth': "80px",

            'params': [
                {
                    'type': 'select',
                    'key': 'baseurl',
                    'label': '测试环境',
                    'width': '200px',
                    # size对应elementui的size，取值为：medium / small / mini
                    'size': 'small',
                    'options': [{'key': obj['id'], 'label': obj['name']} for obj in BASEURL.objects.all().values()]
                }
            ]
        }

    def runstep(self,request,query_set):
        try:
            teststeps = self.gen_teststep(request,query_set)
            runstep_task.delay(teststeps)
        except Exception as e:
            return JsonResponse(data={
                'status': 'error',
                'msg': f'异常！{e}'
            })
        return JsonResponse(data={
            'status': 'success',
            'msg': '处理成功！'
        })

    runstep.short_description = '运行teststep'
    runstep.layer = {
        # 这里指定对话框的标题
        'title': '运行teststep',
        # 提示信息
        'tips': '运行teststep',
        # 确认按钮显示文本
        'confirm_button': '确定',
        # 取消按钮显示文本
        'cancel_button': '取消',

        # 弹出层对话框的宽度，默认50%
        'width': '40%',

        # 表单中 label的宽度，对应element-ui的 label-width，默认80px
        'labelWidth': "80px",

        'params': [
            {
                'type': 'select',
                'key': 'baseurl',
                'label': '测试环境',
                'width': '200px',
                # size对应elementui的size，取值为：medium / small / mini
                'size': 'small',
                'options': [{'key': obj['id'], 'label': obj['name']} for obj in BASEURL.objects.all().values()]
            }
        ]
    }

class Teststepsinline(admin.TabularInline):
    '''
    排序运行测试用例的内联关系
    '''
    model = Teststeps_through
    extra = 0
    autocomplete_fields = ['teststep']

class Parametersinline(admin.TabularInline):
    '''
    排序运行测试用例的内联关系
    '''
    model = Parameters_through
    extra = 0

    @admin.register(Paramkey)
    class ParamkeyAdmin(admin.ModelAdmin):
        # 注册自定义模块
        search_fields = ['value']

        def has_module_permission(self, request):
            return False

    @admin.register(Paramval)
    class ParamvalAdmin(admin.ModelAdmin):
        # 注册自定义模块
        search_fields = ['value']

        def has_module_permission(self, request):
            return False


class TestCaseResource(resources.ModelResource):
    class Meta:
        model = TestCase
        exclude = ('updatetime','createtime')

def get_params(filepath):
    with open(filepath) as f:
        csvdata = csv.reader(f)
        parameters = []
        for row in csvdata:
            parameters.append(row)
    return parameters

def get_testsuite(suitename,obj):
    parameters = {}
    if obj.paramfile.name not in ['',None]:
        params = get_params(obj.paramfile.path)
        paramkeys = '-'.join(params[0])
        parameters_list = params[1:]
        parameters = {paramkeys: parameters_list}
    else:
        for pobj in Parameters_through.objects.filter(testcase=obj):
            parameters[pobj.paramkey.value] = pobj.paramval.value

    config = {}
    config['name'] = obj.name
    config['base_url'] = obj.baseurl.url

    export = []
    for eobj in obj.export.all():
        export.append(eobj.extractkey.value)
    config['export'] = export

    variables = {}
    for vobj in Variables_through.objects.filter(testcase=obj):
        variables[vobj.variablekey.value] = vobj.variableval
    config['variables'] = variables
    teststeps = []
    for teststepobj in obj.teststeps_through_set.all().order_by('runno'):
        teststep = get_teststep(teststepobj.teststep)
        teststeps.append(teststep)
    return {'suitename': suitename, 'config': config, 'teststeps': teststeps, 'parameters': parameters}


@admin.register(TestCase)
class TestCaseAdmin(ImportExportActionModelAdmin, AjaxAdmin):
    # 注册自定义模块
    resource_class = TestCaseResource
    list_display = ['name','creater']
    inlines = [Parametersinline,Variablesinline,Teststepsinline]
    actions = ['gen_yaml','runtestcase']
    exclude = ['creater']
    list_filter = ['creater']
    search_fields = ['name']

    def save_model(self, request, obj, form, change):
        if change is False:
            obj.creater = request.user
        super().save_model(request, obj, form, change)

    def gen_testcases(self,request,query_set):
        post = request.POST
        testcases= []
        for obj in query_set:
            testsuite = get_testsuite('临时测试集',obj)
            testcases.append(testsuite)
        return testcases


    def gen_yaml(self,request,query_set):
        '''
        获取选中测试集合中的用例数据拼接为数组
        :param request:
        :param query_set:
        :return:
        '''
        try:
            testcases = self.gen_testcases(request,query_set)

            with open(filedir + f'/runner/data/test.yaml', 'w', encoding='utf-8') as f:
                yaml.dump(testcases, f, Dumper=yaml.RoundTripDumper)
        except Exception as e:
            return JsonResponse(data={
                'status': 'error',
                'msg': f'异常！{e}'
            })
        return JsonResponse(data={
            'status': 'success',
            'msg': '处理成功！'
        })
    gen_yaml.short_description = '生成文件'
    gen_yaml.layer = {
            # 这里指定对话框的标题
            'title': '生成文件',
            # 提示信息
            'tips': '生成YAML文件',
            # 确认按钮显示文本
            'confirm_button': '确定',
            # 取消按钮显示文本
            'cancel_button': '取消',

            # 弹出层对话框的宽度，默认50%
            'width': '40%',

            # 表单中 label的宽度，对应element-ui的 label-width，默认80px
            'labelWidth': "80px",

            'params': [
            ]
        }

    def runtestcase(self,request,query_set):
        '''
        获取选中测试集合中的用例数据拼接为数组
        :param request:
        :param query_set:
        :return:
        '''
        try:
            testcases = self.gen_testcases(request,query_set)
            runtestcase_task.delay(testcases)
        except Exception as e:
            TESTREPORT.objects.create(reportname=datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '测试报告',
                                      result='N')
            return JsonResponse(data={
                'status': 'error',
                'msg': f'异常！{e}'
            })
        return JsonResponse(data={
            'status': 'success',
            'msg': '处理成功！'
        })
    runtestcase.short_description = '运行用例'
    runtestcase.layer = {
            # 这里指定对话框的标题
            'title': '运行用例',
            # 提示信息
            'tips': '运行用例',
            # 确认按钮显示文本
            'confirm_button': '确定',
            # 取消按钮显示文本
            'cancel_button': '取消',

            # 弹出层对话框的宽度，默认50%
            'width': '40%',

            # 表单中 label的宽度，对应element-ui的 label-width，默认80px
            'labelWidth': "80px",

            'params': [
            ]
        }

class TestSuiteResource(resources.ModelResource):
    class Meta:
        model = TestSuite
        exclude = ('updatetime','createtime')

@admin.register(TestSuite)
class TestSuiteAdmin(ImportExportActionModelAdmin, AjaxAdmin):
    # 注册自定义模块
    resource_class = TestSuiteResource
    list_display = ['name','creater']
    actions = ['gen_yaml','runtestsuite']
    filter_horizontal = ['testcases']
    exclude = ['creater']

    def save_model(self, request, obj, form, change):
        if change is False:
            obj.creater = request.user
        super().save_model(request, obj, form, change)

    def gen_testcases(self,request,suite_obj):
        post = request.POST
        testcases= []
        for obj in suite_obj.testcases.all():
            testsuite = get_testsuite(suite_obj.name, obj)
            testcases.append(testsuite)
        return testcases


    def gen_yaml(self,request,query_set):
        '''
        获取选中测试集合中的用例数据拼接为数组
        :param request:
        :param query_set:
        :return:
        '''
        try:
            testsuite = []
            for obj in query_set:
                testcases = self.gen_testcases(request,obj)
                testsuite.extend(testcases)
            with open(filedir + f'/runner/data/test.yaml', 'w', encoding='utf-8') as f:
                yaml.dump(testsuite, f, Dumper=yaml.RoundTripDumper)
        except Exception as e:
            return JsonResponse(data={
                'status': 'error',
                'msg': f'异常！{e}'
            })
        return JsonResponse(data={
            'status': 'success',
            'msg': '处理成功！'
        })
    gen_yaml.short_description = '生成文件'
    gen_yaml.layer = {
            # 这里指定对话框的标题
            'title': '生成文件',
            # 提示信息
            'tips': '生成YAML文件',
            # 确认按钮显示文本
            'confirm_button': '确定',
            # 取消按钮显示文本
            'cancel_button': '取消',

            # 弹出层对话框的宽度，默认50%
            'width': '40%',

            # 表单中 label的宽度，对应element-ui的 label-width，默认80px
            'labelWidth': "80px",

            'params': [
            ]
        }

    def runtestsuite(self,request,query_set):
        '''
        获取选中测试集合中的用例数据拼接为数组
        :param request:
        :param query_set:
        :return:
        '''
        try:
            runmode = request.POST['runmode']
            testsuite = []
            for obj in query_set:
                testcases = self.gen_testcases(request, obj)
                testsuite.extend(testcases)
            runtestsuite_task.delay(runmode,testsuite)
        except Exception as e:
            TESTREPORT.objects.create(reportname=datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '测试报告',
                                       result='N')
            return JsonResponse(data={
                'status': 'error',
                'msg': f'异常！{e}'
            })
        return JsonResponse(data={
            'status': 'success',
            'msg': '处理成功！'
        })
    runtestsuite.short_description = '运行套件'
    runtestsuite.layer = {
            # 这里指定对话框的标题
            'title': '运行用例',
            # 提示信息
            'tips': '运行用例',
            # 确认按钮显示文本
            'confirm_button': '确定',
            # 取消按钮显示文本
            'cancel_button': '取消',

            # 弹出层对话框的宽度，默认50%
            'width': '40%',

            # 表单中 label的宽度，对应element-ui的 label-width，默认80px
            'labelWidth': "80px",

            'params': [
                {
                    'type': 'select',
                    'key': 'runmode',
                    'label': '运行模式',
                    'width': '200px',
                    # size对应elementui的size，取值为：medium / small / mini
                    'size': 'small',
                    'require': True,
                    'options': [{'key': 'local', 'label': '本地运行'}]
                }
            ]
        }

@admin.register(TESTREPORT)
class TESTREPORTAdmin(admin.ModelAdmin):
    # 注册自定义模块
    list_display = ['reportname','result']